# 55. 网络编程大作业 - 类网盘项目

# 要求

  1. 登录,注册功能
    1. 登录,就是让程序检查用户文件,查看有没有这用户
    2. 注册功能,比较多
      1. 客户端的密码传输,需要使用用户名进行加盐
      2. 服务端接到用户端的密码传输后,不能马上写入用户文件中,需要在服务端在加盐,加盐的值,可以写到配置文件中
      3. 用户在注册中,密码至少达到8位数,其他限制自由
      4. 用户成功注册后,要在服务端的资源目录中创建用户的用户名目录,作为这个用户的云盘存储目录
  2. 主体功能
    1. 输入 .. 就代表要进入上一层目录
      1. 注意:这里最前的目录为用户初始目录,不能超过这个目录
    2. 输入 目录名 就代表进入这个目录
    3. 输入 文件名 就代表下载这个文件
      1. 用户下载到的文件,让用户选择
        1. 默认是,下载到程序的资源目录中
        2. 可以更改下载路径,需要用户输入绝对路径
    4. 输入 up 就代表上传文件
    5. 输入 mk 就代表在当前目录中,创建一个目录
  3. 服务端架构
    1. 启动文件,需要使用 IO多路复用架构
    2. 核心文件,使用面向对象跟函数编写
    3. 启动文件调用核心文件时,需要使用线程池调用,保证这程序可以多人连接
    4. socker模块,使用默认的TCP协议
    5. 可以加上日志,不要求,博主也只加了日志的模板,懒得一一去加日志了
  4. 客户端架构
    1. 客户端没什么要求,就核心文件,需要按面向对象编写

# 服务端

MkzhJU.png

# 启动文件 - server.py

#! /usr/bin/env python
# -*- coding:utf-8 -*-
from __future__ import division

from concurrent.futures import ThreadPoolExecutor
import select
import socket
import sys
import os
import logging
server_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(server_path)
from conf import conf
from core import core

## 创建一个对象(实例化logging模块中的getLogger()类)
logger = logging.getLogger()
## 创建一个文件管理操作符
fh = logging.FileHandler("../log/logging.log",encoding="utf-8")
## 创建一个屏幕管理操作符
sh = logging.StreamHandler()
## 创建一个日志输出的格式
journal1 = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
## 文件管理操作符 绑定 日志格式
fh.setFormatter(journal1)
## 屏幕管理操作符 绑定 日志格式
sh.setFormatter(journal1)
## 设置日志输出等级
logger.setLevel(logging.DEBUG)
## 创建好的对象 绑定 文件管理操作符
logger.addHandler(fh)
## 创建好的对象 绑定 屏幕管理操作符
logger.addHandler(sh)

sk = socket.socket()
sk.bind((conf.ip,conf.port))
sk.listen()

server_del = []  ## 要删除的列表
server_r = [sk]  ## 监听的列表

while 1:
    r,w,x = select.select(server_r,[],[])
    if r:
        for i in r:
            if i == sk:
                conn,addr = i.accept()
                server_r.append(conn)
                print(server_r)
            else:
                print(i)
                te = ThreadPoolExecutor(20)
                te.submit(core.server_user,i,logger,conf.mi)
                server_del.append(i)
        if server_del:
            for s in server_del:
                server_r.remove(s)
            server_del.clear() 

# 核心文件 - core.py

#! /usr/bin/env python
# -*- coding:utf-8 -*-
from __future__ import division
import struct
import json
import hashlib
import os

if __name__ == '__main__':
    import socket

    sk = socket.socket()
    sk.bind(("127.0.0.1",18080))
    sk.listen()

class tool():
    def __init__(self):
        pass

    def json(self,num,ty="dumps"):
        if ty == "dumps":
            user_json = json.dumps(num)
            user_b = struct.pack("i", len(user_json))
            return user_json, user_b

    def struct(self,con):
        server_struct_size = struct.unpack("i", con.recv(4))[0]
        server_struct_user = json.loads(con.recv(server_struct_size).decode("utf-8"))
        return server_struct_user

class user():
    def __init__(self,con,mi,to):
        self.con = con
        self.mi = mi
        self.to = to

    def Registration(self):
        self.con.send("1".encode("utf-8"))
        while 1:
            user, paawd = self.to.struct(self.con)
            Registration_user_vs = 1
            with open("../conf/user") as f:
                for i in f:
                    f_user, f_pawwd = i.strip().split("|")
                    if f_user == user:
                        self.con.send("2".encode("utf-8"))
                        Registration_user_vs = 0
            if Registration_user_vs == 0:
                continue
            md5 = hashlib.md5(self.mi.encode("utf-8"))
            md5.update(paawd.encode("utf-8"))
            md5_paawd = md5.hexdigest()
            with open("../conf/user", "a") as f:
                f.write("%s|%s" % (user, md5_paawd))
                f.write("\n")
            self.con.send("1".encode("utf-8"))
            os.mkdir("../data/%s" % user)
            break

    def register(self):
        while 1:
            self.con.send("1".encode("utf-8"))
            user, paawd = self.to.struct(self.con)
            md5 = hashlib.md5(self.mi.encode("utf-8"))
            md5.update(paawd.encode("utf-8"))
            paawd = md5.hexdigest()
            with open("../conf/user") as f:
                for i in f:
                    f_user, f_pawwd = i.strip().split("|")
                    if user == f_user:
                        if paawd == f_pawwd:
                            self.con.send("1".encode("utf-8"))
                            return 1
                        else:
                            self.con.send("2".encode("utf-8"))
                            break

class operation():
    def __init__(self,con,to):
        self.con = con
        self.to = to

    def Capacity(self):
        while 1:
            capacity_user = self.to.struct(self.con)
            user_list = []
            if len(capacity_user) == "1":
                capacity_user = capacity_user[0]
            else:
                capacity_user = "/".join(capacity_user)
            for i in os.listdir("../data/%s" % capacity_user):
                if os.path.isdir("../data/%s/%s" % (capacity_user, i)):
                    user_list.append("\033[32m%s\033[0m" % i)
                else:
                    user_list.append(i)
            user_list.append(capacity_user)
            cc_json, cc_b = self.to.json(user_list)
            self.con.send(cc_b + cc_json.encode("utf-8"))
            behavior = self.to.struct(self.con)
            if behavior == "1":
                continue
            elif behavior == "upload":
                self.upload(user_list)
            elif behavior == "mkdir":
                self.mkdir(capacity_user)
            elif behavior == "download":
                self.download(capacity_user)

    def upload(self,user_list):
        self.con.send("1".encode("utf-8"))
        up_user, up_size, up_md5 = self.to.struct(self.con)
        up_directory = user_list.pop()
        up_u = 0
        for i in user_list:
            if up_user == i:
                self.con.send("9".encode("utf-8"))
                break
            elif up_user == "\033[32m%s\033[0m" % i:
                self.con.send("9".encode("utf-8"))
                break
        else:
            self.con.send("8".encode("utf-8"))
            up_u = 1
        if up_u:
            up_directory = "../data/%s/%s" % (up_directory, up_user)
            with open(up_directory, "ab") as f:
                while up_size:
                    content = self.con.recv(1024)
                    f.write(content)
                    up_size -= len(content)
            up_md5_1 = hashlib.md5()
            with open(up_directory, "rb") as f:
                for i in f:
                    up_md5_1.update(i)
                up_md5_1 = up_md5_1.hexdigest()
            if up_md5 == up_md5_1:
                self.con.send("4".encode("utf-8"))
            else:
                self.con.send("5".encode("utf-8"))
                os.remove("up_directory")

    def mkdir(self,mk_initial):
        self.con.send("1".encode("utf-8"))
        mk_file = self.to.struct(self.con)
        if not os.path.exists("../data/%s/%s"%(mk_initial,mk_file)):
            os.mkdir("../data/%s/%s"%(mk_initial,mk_file))
            if os.path.isdir("../data/%s/%s"%(mk_initial,mk_file)):
                self.con.send("2".encode("utf-8"))
            else:
                self.con.send("9".encode("utf-8"))
                if os.path.exists("../data/%s/%s"%(mk_initial,mk_file)):
                    os.remove("../data/%s/%s"%(mk_initial,mk_file))
        else:
            self.con.send("8".encode("utf-8"))

    def download(self,capacity_user):
        self.con.send("1".encode("utf-8"))
        do_1 = self.con.recv(1).decode("utf-8")
        if do_1 == "2":
            do_file = self.to.struct(self.con)
            do_file_list = []
            do_file_size = os.path.getsize("../data/%s/%s"%(capacity_user,do_file))
            up_md5 = hashlib.md5()
            with open(os.path.abspath("../data/%s/%s"%(capacity_user,do_file)), "rb") as f:
                for i in f:
                    up_md5.update(i)
                up_md5 = up_md5.hexdigest()
            do_file_list.extend([do_file_size,up_md5])
            do_json,do_b = self.to.json(do_file_list)
            self.con.send(do_b + do_json.encode("utf-8"))
            with open(os.path.abspath("../data/%s/%s"%(capacity_user,do_file)), "rb") as f:
                while do_file_size:
                    content = f.read(1024)
                    self.con.send(content)
                    do_file_size -= len(content)

def server_user(con,logge,mi):
    while 1:
        server_user_size = struct.unpack("i",con.recv(4))[0]
        server_user = json.loads(con.recv(server_user_size).decode('utf-8'))
        to = tool()
        u = user(con,mi,to)
        o = operation(con,to)
        if server_user[1] == "register":
            # rg = register(con,mi)
            u.register()
            o.Capacity()
        elif server_user[1] == "Registration":
            # Registration(con,mi)
            u.Registration()

if __name__ == '__main__':
    conn,addr = sk.accept()
    server_user(conn,1,"网盘")

# 配置文件

# 主配置文件

#! /usr/bin/env python
# -*- coding:utf-8 -*-
from __future__ import division

## 服务器IP
ip = "127.0.0.1"

## 服务器端口
port = 18080

## 服务端对密码进行第二次加盐
mi = "网盘"

# 用户文件

jc|7e990c6f24d73f5fb556c31b1107634f

# 客户端

Mkz4WF.png

# 启动文件 - client.py

#! /usr/bin/env python
# -*- coding:utf-8 -*-
from __future__ import division

import socket
import sys
import os
server_path = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
sys.path.append(server_path)
from core import core

while 1:
    print("=" * 30)
    print("请选择您要进行的操作:")
    rs = {1:"登录",2:"注册",3:"退出"}
    print("="*30)
    [print("%s:%s"%(k,v)) for k,v in rs.items()]
    user_input  = input(">>>:")
    if user_input.isalpha():
        continue
    elif user_input == "1":
        register = core.user("register")
        rst1,rst_user = register.register()
        if rst1 == "1":
            print("=" * 30)
            print("默认进入并打开用户的当前空间")
            register.Capacity(rst_user)

    elif user_input == "2":
        Registration = core.user("Registration")
        Registration.Registration()

    elif user_input == "3":
        print("=" * 30)
        print("程序退出")
        exit()
    else:
        continue

# 核心文件 - core.py

#! /usr/bin/env python
# -*- coding:utf-8 -*-
from __future__ import division

import socket
import sys
import os
import json
import struct
import hashlib
server_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(server_path)
from conf import conf

sk = socket.socket()
sk.connect((conf.ip,conf.port))

class user:
    def __init__(self,user):
        self.user = user
        self.user_list = ["user"]

    def json(self,num,ty="dumps"):
        if ty == "dumps":
            user_json = json.dumps(num)
            user_b = struct.pack("i", len(user_json))
            return user_json,user_b

    def struct(self):
        struct_size = struct.unpack("i", sk.recv(4))[0]
        struct_user = json.loads(sk.recv(struct_size).decode("utf-8"))
        return struct_user

    def hashlib(self,user,pawwd):
        md5 = hashlib.md5(user.encode("utf-8"))
        md5.update(pawwd.encode("utf-8"))
        pawwd = md5.hexdigest()
        return pawwd

    def run(self):
        self.user_list.append(self.user)
        user_json,user_b = self.json(self.user_list)
        sk.send(user_b + user_json.encode("utf-8"))
    def register(self):
        self.run()
        rs = sk.recv(1).decode("utf-8")
        while 1:
            if rs:
                rs_user_list = []
                rs_user = input("用户名:")
                rs_pass = input("密码:")
                rs_pass = self.hashlib(rs_user,rs_pass)
                rs_user_list.extend([rs_user, rs_pass])
                rs_json,rs_b = self.json(rs_user_list)
                sk.send(rs_b + rs_json.encode("utf-8"))
                rs1 = sk.recv(1).decode("utf-8")
                if rs1 == "1":
                    print("=" * 30)
                    print("登录成功")
                    return rs1,rs_user
                elif rs1 == "2":
                    print("=" * 30)
                    print("用户名或密码错误")
                    continue
                else:
                    print("=" * 30)
                    print("登录失败")
                    continue
            else:
                print("=" * 30)
                print("远程服务器,无响应")
                break

    def Registration(self):
        self.run()
        rt = sk.recv(1).decode("utf-8")
        while 1:
            if rt:
                rt_user_list = []
                rt_user = input("用户名:")
                rt_pass = input("密码:")
                rt_pass1 = input("确定密码:")
                if not rt_pass == rt_pass1:
                    print("=" * 20)
                    print("二次输入的密码不相同,请重新注册")
                    continue
                elif len(rt_pass) < 8:
                    print("="*20)
                    print("密码长度最低为8位数")
                    continue
                rt_pass = self.hashlib(rt_user,rt_pass)
                rt_user_list.extend([rt_user,rt_pass])
                rt_json, rt_b = self.json(rt_user_list)
                sk.send(rt_b + rt_json.encode("utf-8"))
                rt_i = sk.recv(1).decode("utf-8")
                if rt_i == "1":
                    print("=" * 30)
                    print("注册成功")
                    break
                elif rt_i == "2":
                    print("=" * 30)
                    print("用户名重复,请重新注册")
                    continue
                else:
                    print("=" * 30)
                    print("注册出错")
                    break
            else:
                print("=" * 30)
                print("远程服务器,无响应")
                break

    def upload(self):
        rt = sk.recv(1).decode("utf-8")
        if rt:
            if rt == "1":
                print("=" * 85)
                up = input("请输入你要上传文件的绝对路径:")
                print("=" * 85)
                up_user = input("是否要使用旧文件名为上传文件名,如果是请直接回车,如果不是请输入新文件名:")
                if os.path.exists(os.path.abspath(up)):
                    if os.path.isfile(up):
                        up_list = []
                        if not up_user:
                            up_path_file = os.path.basename(up)
                        else:
                            up_path_file = up_user
                        up_path_size = os.path.getsize(up)
                        up_md5 = hashlib.md5()
                        with open(os.path.abspath(up),"rb") as f:
                            for i in f:
                                up_md5.update(i)
                            up_md5 = up_md5.hexdigest()
                        up_list.extend([up_path_file,up_path_size,up_md5])
                        up_json,up_b = self.json(up_list)
                        sk.send(up_b + up_json.encode("utf-8"))
                        rt_2 = sk.recv(1).decode("utf-8")
                        if rt_2:
                            if rt_2 == "8":
                                if up_path_size > 1024:
                                    rt_plan_1 = 100 / round(up_path_size / 1024)
                                    rt_plan_2 = 1
                                else:
                                    rt_plan_2 = 0
                                with open(os.path.abspath(up),"rb") as f:
                                    print("=" * 85)
                                    while up_path_size:
                                        content = f.read(1024)
                                        sk.send(content)
                                        if rt_plan_2:
                                            print('\r' + rt_plan_2 * '=' + '>' + str(rt_plan_2 * rt_plan_1) + '%', end='')
                                            rt_plan_2 += 1
                                        else:
                                            print('\r' + 1 * '=' + '>' + "100" + '%', end='')
                                        up_path_size -= len(content)
                                print('')
                                rt_3 = sk.recv(1).decode("utf-8")
                                if rt_3:
                                    if rt_3 == "4":
                                        print("=" * 30)
                                        print("上传文件成功")
                                    elif rt_3 == "5":
                                        print("=" * 30)
                                        print("上传文件异常,请重新上传")
                                    else:
                                        print("=" * 30)
                                        print("服务器出现异常错误")
                                else:
                                    print("=" * 30)
                                    print("服务器无响应")
                            elif rt_2 == "9":
                                print("=" * 30)
                                print("文件名重复")
                            else:
                                print("=" * 30)
                                print("服务器出现异常错误")
                        else:
                            print("=" * 30)
                            print("服务器无响应")

                    else:
                        print("=" * 85)
                        print("要上传的是目录,不是文件,本程序只支持上传文件,请您谅解")
                else:
                    print("=" * 30)
                    print("输入的文件路径有误")
        else:
            print("=" * 30)
            print("服务器无响应")

    def mkdir(self):
        rt = sk.recv(1).decode("utf-8")
        if rt:
            if rt == "1":
                print("=" * 85)
                mk_inpu = input("请输入目录名:")
                mk_json,mk_b = self.json(mk_inpu)
                sk.send(mk_b + mk_json.encode("utf-8"))
                rt_2 = sk.recv(1).decode("utf-8")
                if rt_2:
                    if rt_2 == "2":
                        print("=" * 85)
                        print("创建目录成功")
                    elif rt_2 == "8":
                        print("=" * 85)
                        print("创建的目录名重复")
                    elif rt_2 == "9":
                        print("=" * 85)
                        print("系统错误,创建目录失败")
                else:
                    print("=" * 85)
                    print("服务器无响应")
        else:
            print("=" * 85)
            print("服务器无响应")

    def download(self,file):
        rt = sk.recv(1).decode("utf-8")
        if rt:
            if rt == "1":
                do_path = input("是否要已默认为程序中的data目录为下载路径,是直接回车,如想下载到别处,请输入下载绝对路径:")
                do_file = input("是否要使用旧文件名为下载文件名,是请直接回车,如果不是,请输入新文件名:")
                if not do_file:
                    do_file = file
                if not do_path:
                    do_path = "../data/"
                if not os.path.isdir(os.path.abspath(do_path)):
                    print("输入的路径不是一个目录,请重新下载")
                else:
                    do_path_file = os.path.abspath(os.path.join(do_path,do_file))
                    if os.path.exists(do_path_file):
                        print("文件名跟路径中的目录名或文件名相同,请重新下载")
                        sk.send("9".encode("utf-8"))
                    else:
                        sk.send("2".encode("utf-8"))
                        do_json, do_b = self.json(file)
                        sk.send(do_b + do_json.encode("utf-8"))
                        do_size,do_md5 = self.struct()
                        with open(do_path_file, "ab") as f:
                            while do_size:
                                content = sk.recv(1024)
                                f.write(content)
                                do_size -= len(content)
                        od_md5_1 = hashlib.md5()
                        with open(do_path_file, "rb") as f:
                            for i in f:
                                od_md5_1.update(i)
                            od_md5_1 = od_md5_1.hexdigest()
                        if do_md5 == od_md5_1:
                            print("文件下载成功")
                        else:
                            print("文件下载失败")
                            os.remove(do_path_file)
        else:
            print("=" * 30)
            print("服务器无响应")

    def Capacity(self,user):
        user_list = [user]
        while 1:
            cc_json,cc_b = self.json(user_list)
            sk.send(cc_b + cc_json.encode("utf-8"))
            cc_list = self.struct()
            print("=" * 85)
            print("""
温馨提醒:
    1. 黄色为目录
    2. 白色为文件
    3. 输入"文件名"默认下载
    4. 输入"目录名"默认进入该目录
    5. 输入".." 二个小数点,默认返回上级目录
    6. 上传:请输入"up",上传的路径,默认在选择上传时的目录中
    7. 创建目录:请输入"mk",创建的目录,默认在选择创建时的目录中
    注意:本程序不支持上传目录跟下载目录,如果需要上传或下载目录,请自行打包后上传
            """)
            print("=" * 85)
            mcd = cc_list.pop()
            print("=" * 20 + mcd + "=" * 20 )
            if cc_list:
                [print(i) for i in cc_list]
            else:
                print("=" * 30)
                print("空")
            while 1:
                cc_import = input(">>>:")
                if cc_import == "up":
                    cc_json,cc_b = self.json("upload")
                    sk.send(cc_b + cc_json.encode("utf-8"))
                    self.upload()
                    break
                elif cc_import == "mk":
                    cc_json, cc_b = self.json("mkdir")
                    sk.send(cc_b + cc_json.encode("utf-8"))
                    self.mkdir()
                    break
                elif cc_import == "..":
                    if len(user_list) == 1 or len(user_list) == 0:
                        cc_json, cc_b = self.json("1")
                        sk.send(cc_b + cc_json.encode("utf-8"))
                        print("=" * 30)
                        print("当前已在用户的最前目录")
                        break
                    else:
                        cc_json, cc_b = self.json("1")
                        sk.send(cc_b + cc_json.encode("utf-8"))
                        user_list.pop()
                        break
                else:
                    for i in cc_list:
                        if cc_import == i:
                            cc_json, cc_b = self.json("download")
                            sk.send(cc_b + cc_json.encode("utf-8"))
                            self.download(cc_import)
                            user_ok = 1
                            break

                        elif i == '\x1b[32m%s\x1b[0m'%cc_import:
                            cc_json, cc_b = self.json("1")
                            sk.send(cc_b + cc_json.encode("utf-8"))
                            user_list.append(cc_import)
                            user_ok = 1
                            break
                    else:
                        cc_json, cc_b = self.json("1")
                        sk.send(cc_b + cc_json.encode("utf-8"))
                        print("=" * 30)
                        print("输入有误")
                        user_ok = 0
                        continue
                if user_ok:
                    break

# 主配置文件

#! /usr/bin/env python
# -*- coding:utf-8 -*-
from __future__ import division

## 连接服务IP
ip = "127.0.0.1"

## 连接服务端口
port = 18080